<?php
//
//  VictoryCMS - Content managment system and framework.
//  Copyright (C) 2009  Lewis Gunsch
//
//  This file is part of VictoryCMS.
//  
//  VictoryCMS is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  VictoryCMS is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//  
//  You should have received a copy of the GNU General Public License
//  along with VictoryCMS.  If not, see <http://www.gnu.org/licenses/>.

/**
 * VictoryCMS main startup. This is the entry point to the VictoryCMS
 * system. It initializes the error handlers, the class autoloader and 
 * contains some important global functions. 
 * 
 * <strong>Note:</strong> This depends on <strong>FolderMap.php</strong> for 
 * mapping folders to concrete folders on the file system. This also depends
 * on <strong>VarMap.php</strong> for storing system variables. These should be 
 * in the same directory as this file.
 *
 * @author Lewis Gunsch
 * @filesource
 * @package VcmsCore
 * @license http://www.gnu.org/licenses/gpl.html
 */


//ONLY FOR TESTING///////////////////
//ini_set('display_errors', '1');
//error_reporting(E_STRICT);
/////////////////////////////////////

spl_autoload_register('vcms_class_autoloader');
set_exception_handler('errorHandler');
set_error_handler('errorHandler', E_STRICT);

require_once('FolderMap.php');
require_once('VarMapKeys.php');
require_once('VarMap.php');

/**
 * An simplle error handler used by VictoryCMS to capture php errors.
 *
 * @see http://www.php.net/set_error_handler
 
function VcmsErrorHandler($errno, $errstr, $errfile, $errline, array $errcontext) {
	echo ' in <strong>'.$errFile.' on line $errline</strong><br />';
	echo '<em>'.$errstr.'</em><br />';
	echo '<strong>Active symbol table: </strong>';
	echo print_ar($errcontext) .'<br />';
	return false;
}*/

/*
 * An exception handler used in the class_autoloader to return errors.
 *
 * @see http://www.php.net/manual/en/function.set-exception-handler.php
 *
function VcmsExceptionHandler($exception) {

	echo 'An Exception has occured in <strong>'.$exception->getFile()
	.'</strong> on line <strong>'. $exception->getLine() .'</strong>:<br />'. 
	$exception->getMessage() .'<br />';
}*/

/**
 * An class autoloader for loading classes from the include directory.
 * @return boolean true if class was loaded, and false if not.
 */
function vcms_class_autoloader($class) {

	if (class_exists($class, false) || interface_exists($class, false)) {
		return true;
	}
	
	if (file_exists(FolderMap::getIncludeFolder().$class.'.php')) {
		require_once(FolderMap::getIncludeFolder().$class.'.php');
		
	} else if (file_exists(FolderMap::getDALFolder().$class.'.php')) {
		require_once(FolderMap::getDALFolder().$class.'.php');

	} else if (file_exists(FolderMap::getHALFolder().$class.'.php')) {
		require_once(FolderMap::getHALFolder().$class.'.php');
	
	} else if (file_exists(FolderMap::getQuerySetFolder().$class.'.php')) {
		require_once(FolderMap::getQuerySetFolder().$class.'.php');
	
	} else if (file_exists(FolderMap::getExeceptionFolder().$class.'.php')) {
		require_once(FolderMap::getExeceptionFolder().$class.'.php');

	} else if (file_exists(FolderMap::getComponentFolder().$class.'.php')) {
		require_once(FolderMap::getComponentFolder().$class.'.php');

	} else if (file_exists(FolderMap::getModelsFolder().$class.'.php')) {
		require_once(FolderMap::getModelsFolder().$class.'.php');
	
	} else {
		return false;
	}
	return true;
}

/**
 * This loads a class from a specified folder path.
 * The folder should contain the trailing slash.
 *
 * @param $class The class to load.
 * @param $folder The folder path with trialing slashes.
 * @return boolean True if class was loaded correctly and false if not.
 *
 * @todo handle slashes intelligently
 */
function loadFromFolder($class, $folder) {

	if (class_exists($class, false) || interface_exists($class, false)) {
		return true;
	}
	$file_path = $folder.$class.'.php';
	if (file_exists($file_path)) {
		require_once($file_path);
		return true;
	} else {
		return false;
	}
}

/**
 * The main startup method for initializing VictoryCMS.
 * This should be called with the path to the settings.xml
 * set. This is normally called from the index.php file, or default
 * start page.
 * @param $settingsPath
 * @return void
 *
 * @todo Finish implementing the startup.
 */
function VcmsStartup() {
	
	// enable sessions here for other pages.
	session_start();
		
	$pageBuilder = PageBuilderFactory::createPageBuilder('TestPageBuilder');
	VarMap::attach($pageBuilder, VarMapKeys::curr_pagebuilder);
	$pageBuilder->render();
}

/**
 * Return the current PageBuilder object.
 * @return PageBuilder The current PageBuilder object.
 */
function getCurrPageBuilder() {
	return VarMap::get(VarMapKeys::curr_pagebuilder);
}

/**
 * An error handler for Vcms for exceptions and php errors. It will
 * print out the file and line number, as well as a back trace and
 * the current php context.
 * 
 * @todo let the user configure their own pulblic error message via Settings.xml
 * @todo the admin email should be set in the Settings.xml file, not here.
 * 
 * @see http://www.php.net/set_error_handler
 */
function errorHandler($errno, $errstr='', $errfile='', $errline='')
{
	// if error has been supressed with an @
	if (error_reporting() == 0) {
		return;
	}
	
	// get the necessary varaibles from the VarMap
	$debugging = VarMap::get(VarMapKeys::debug_enabled);
	$email_path = VarMap::get(VarMapKeys::admin_email);

	//echo "Debugging: $debugging and Email path: $email_path <br />";
	
	// check if function has been called by an exception
	if(func_num_args() == 5) {
		// called by trigger_error()
		$exception = null;
		list($errno, $errstr, $errfile, $errline) = func_get_args();
		$backtrace = array_reverse(debug_backtrace());

	} else {
		// caught exception
		$exc = func_get_arg(0);
		$errno = $exc->getCode();
		$errstr = $exc->getMessage();
		$errfile = $exc->getFile();
		$errline = $exc->getLine();
		$backtrace = $exc->getTrace();
	}

	/*
	 * E_RECOVERABLE_ERROR is not supported in PHP 5.1.*
	 * so comment out for now
	 */
	$errorType = array (
	E_ERROR            => 'ERROR',
	E_WARNING        => 'WARNING',
	E_PARSE          => 'PARSING ERROR',
	E_NOTICE         => 'NOTICE',
	E_CORE_ERROR     => 'CORE ERROR',
	E_CORE_WARNING   => 'CORE WARNING',
	E_COMPILE_ERROR  => 'COMPILE ERROR',
	E_COMPILE_WARNING => 'COMPILE WARNING',
	E_USER_ERROR     => 'USER ERROR',
	E_USER_WARNING   => 'USER WARNING',
	E_USER_NOTICE    => 'USER NOTICE',
	E_STRICT         => 'STRICT NOTICE'//,
	//E_RECOVERABLE_ERROR  => 'RECOVERABLE ERROR'
	);

	// create error message
	if (array_key_exists($errno, $errorType)) {
		$err = '<strong>'. $errorType[$errno] .'</strong>';
	} else {
		$err = '<strong>CAUGHT EXCEPTION</strong>';
	}

	$errMsg = "$err: $errstr in <strong>$errfile</strong> on line <strong>$errline</strong>";

	// start backtrace
	foreach ($backtrace as $v) {

		if (isset($v['class'])) {
			$trace = 'in class <strong>'.$v['class'].'::'.$v['function'].'(';
			if (isset($v['args'])) {
				$separator = '';
				foreach($v['args'] as $arg ) {
					$trace .= "$separator".getArgument($arg);
					$separator = ', ';
				}
			}
			$trace .= ')</strong>';
		}

		elseif (isset($v['function']) && empty($trace)) {
			$trace = 'in function <strong>'.$v['function'].'(';
			if (!empty($v['args'])) {

				$separator = '';

				foreach($v['args'] as $arg ) {
					$trace .= "$separator".getArgument($arg);
					$separator = ', ';
				}
			}
			$trace .= ')</strong>';
		}
	}

	// display error msg, if debug is enabled
	if($debugging == true) {
		echo '<h2>Debug Msg</h2>'.nl2br($errMsg).'<br />
            <strong>Trace:</strong> '.nl2br($trace).'<br />';
	}

	// what to do
	switch ($errno) {
		case E_NOTICE:
		case E_USER_NOTICE:
			//return;
			return false;
			break;

		default:
			if($debugging == false) {
				// send email to admin
				if(!empty($email_path)) {
					$headers = 'From: VictoryCMS Error Handler\r\n';
					$subject = 'critical error on '.$_SERVER['HTTP_HOST'];
					@mail($email_path, $subject, $errorText, $headers);
				}
				// end and display error msg
				exit('An error was encountered on this page.');
			}
			else
			exit('<p>Aborting.</p>');
			break;
	}
	return false;
}

/**
 * Creates a string representation of an argument 
 * sent to a function.
 * 
 * @param $arg The argument to output.
 * @return string
 */
function getArgument($arg) {
	switch (strtolower(gettype($arg))) {

		case 'string':
			return( '"'.str_replace( array("\n"), array(''), $arg ).'"' );

		case 'boolean':
			return (bool)$arg;

		case 'object':
			return 'object('.get_class($arg).')';

		case 'array':
			$ret = 'array(';
			$separtor = '';
			foreach ($arg as $k => $v) {
				$ret .= $separtor.getArgument($k).' => '.getArgument($v);
				$separtor = ', ';
			}
			$ret .= ')';
			return $ret;

		case 'resource':
			return 'resource('.get_resource_type($arg).')';

		default:
			return var_export($arg, true);
	}
}

/**
 * JUST FOR TESTING, works the same as print_r() but
 * prints out array's nicely. This should not be used
 * in a production environment.
 *
 * @param $array The array to print out.
 * @param $count Recursive call parameter - DO NOT use directly
 */
function print_ar($array, $count=0) {
	$i=0;
	$tab ='';
	while($i != $count) {
		$i++;
		$tab .= "&nbsp;&nbsp;|&nbsp;&nbsp;";
	}
	$k = 0;
	foreach((array)$array as $key=>$value){
		if(is_array($value)){
			echo $tab."[<strong><u>$key</u></strong>]<br />";
			$count++;
			print_ar($value, $returnText, $count);
			$count--;
		}
		else{
			$tab2 = substr($tab, 0, -12);
			echo "$tab2~ $key: <strong>$value</strong><br />";
		}
		$k++;
	}
	$count--;
}

?>
